﻿using MongoDB.Bson;
using System.Collections.Generic;
using System.Linq;

namespace MongoDB.Driver
{
    /// <summary>
    /// MongoDb Definition拓展
    /// </summary>
    public static class MongoDbDefinitionExtensions
    {
        /// <summary>
        /// 将实体转换为Mongodb UpdateDefinition类型
        /// </summary>
        /// <typeparam name="TDocument">MongoDB文档实体</typeparam>
        /// <typeparam name="TSource">更新数据实体</typeparam>
        /// <param name="source"></param>
        /// <returns></returns>
        public static UpdateDefinition<TDocument> ToUpdateDefinition<TDocument, TSource>(this TSource source)
        {
            List<UpdateDefinition<TDocument>> updateDefinitions = new List<UpdateDefinition<TDocument>>();
            var destProperties = typeof(TDocument).GetProperties().ToList();
            foreach (var srcProp in typeof(TSource).GetProperties())
            {
                var matchingProperty = destProperties.Where(p => p.Name == srcProp.Name).SingleOrDefault();
                if (matchingProperty != null)
                {
                    var value = srcProp.GetValue(source);
                    if (value == null)
                    {
                        continue;
                    }
                    var curUpdate = Builders<TDocument>.Update.Set(matchingProperty.Name, value);
                    updateDefinitions.Add(curUpdate);
                }
            }

            return Builders<TDocument>.Update.Combine(updateDefinitions);
        }

        /// <summary>
        /// 将查询实体转换为MongoDB FilterDefinition实体
        /// </summary>
        /// <typeparam name="TDocument">MongoDB文档实体</typeparam>
        /// <param name="source">查询实体</param>
        /// <returns></returns>
        public static List<FilterDefinition<TDocument>> ToFilterDefinition<TDocument>(this QueryModel source)
        {
            List<FilterDefinition<TDocument>> filterDefinitions = new List<FilterDefinition<TDocument>>();
            var destProperties = typeof(TDocument).GetProperties().ToList();


            var matchingProperty = destProperties.Where(p => p.Name == source.FieldName).SingleOrDefault();
            if (matchingProperty != null)
            {
                switch (source.QueryType)
                {
                    case QueryTypeEnum.Equal:
                        filterDefinitions.Add(Builders<TDocument>.Filter.Eq(matchingProperty.Name, source.FieldValue));
                        break;
                    case QueryTypeEnum.NotEqual:
                        filterDefinitions.Add(Builders<TDocument>.Filter.Ne(matchingProperty.Name, source.FieldValue));
                        break;
                    case QueryTypeEnum.Like:
                        filterDefinitions.Add(Builders<TDocument>.Filter.Regex(matchingProperty.Name, source.FieldValue));
                        break;
                    case QueryTypeEnum.OrLike:
                        string[] orlike_values = source.FieldValue.Split('|');
                        List<FilterDefinition<TDocument>> orlikeList = new List<FilterDefinition<TDocument>>();
                        for (int i = 0; i < orlike_values.Length; i++)
                        {
                            orlikeList.Add(Builders<TDocument>.Filter.Regex(matchingProperty.Name, orlike_values[i]));
                        }
                        filterDefinitions.Add(Builders<TDocument>.Filter.Or(orlikeList));
                        break;
                    case QueryTypeEnum.AndLike:
                        string[] andlike_values = source.FieldValue.Split('|');
                        List<FilterDefinition<TDocument>> andlike_value = new List<FilterDefinition<TDocument>>();
                        for (int i = 0; i < andlike_values.Length; i++)
                        {
                            filterDefinitions.Add(Builders<TDocument>.Filter.Regex(matchingProperty.Name, andlike_values[i]));
                        }
                        break;
                    case QueryTypeEnum.NotLike:
                        string[] notlike_values = source.FieldValue.Split('|');
                        List<FilterDefinition<TDocument>> notlikeList = new List<FilterDefinition<TDocument>>();
                        for (int i = 0; i < notlike_values.Length; i++)
                        {
                            filterDefinitions.Add(Builders<TDocument>.Filter.Not(Builders<TDocument>.Filter.Regex(matchingProperty.Name, notlike_values[i])));
                        }
                        break;
                    case QueryTypeEnum.NULL:
                        FilterDefinition<TDocument> filter = new BsonDocument(matchingProperty.Name, BsonNull.Value);
                        filterDefinitions.Add(Builders<TDocument>.Filter.And(filter));
                        break;
                    case QueryTypeEnum.NOTNULL:
                        filterDefinitions.Add(Builders<TDocument>.Filter.Ne(matchingProperty.Name, BsonNull.Value));
                        break;
                    case QueryTypeEnum.OR:
                        string[] or_values = source.FieldValue.Split('|');
                        List<FilterDefinition<TDocument>> orList = new List<FilterDefinition<TDocument>>();
                        for (int i = 0; i < or_values.Length; i++)
                        {
                            orList.Add(new BsonDocument(matchingProperty.Name, or_values[i]));
                        }
                        filterDefinitions.Add(Builders<TDocument>.Filter.Or(orList));
                        break;
                    case QueryTypeEnum.Greater:
                        filterDefinitions.Add(Builders<TDocument>.Filter.Gt(matchingProperty.Name, source.FieldValue));
                        break;
                    case QueryTypeEnum.GreaterEqual:
                        filterDefinitions.Add(Builders<TDocument>.Filter.Gte(matchingProperty.Name, source.FieldValue));
                        break;
                    case QueryTypeEnum.Less:
                        filterDefinitions.Add(Builders<TDocument>.Filter.Lt(matchingProperty.Name, source.FieldValue));
                        break;
                    case QueryTypeEnum.LessEqual:
                        filterDefinitions.Add(Builders<TDocument>.Filter.Lte(matchingProperty.Name, source.FieldValue));
                        break;
                    case QueryTypeEnum.Between:
                        string[] between_values = source.FieldValue.Split('|');
                        string between_value1 = between_values[0];
                        string between_value2 = between_values[1];
                        filterDefinitions.Add(Builders<TDocument>.Filter.Gt(matchingProperty.Name, between_value1));
                        filterDefinitions.Add(Builders<TDocument>.Filter.Lt(matchingProperty.Name, between_value2));
                        break;
                    default:
                        break;
                }
            }

            return filterDefinitions;
        }
    }
}
